package site.koudai.cloud.aspect;

import com.baomidou.mybatisplus.core.metadata.IPage;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import site.koudai.cloud.annotation.DataResource;
import site.koudai.cloud.common.Result;
import site.koudai.cloud.context.UserContext;
import site.koudai.cloud.entity.Permission;
import site.koudai.cloud.entity.User;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;

@Aspect
@Component
@Slf4j
public class DataResourceAspect {

    @Pointcut("@annotation(site.koudai.cloud.annotation.DataResource)")
    public void dataResourcePointCut() {

    }

    @Around("dataResourcePointCut()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        UserContext.set("requireDataPermission", true);
        return joinPoint.proceed();
    }

    @After("dataResourcePointCut()")
    public void after(JoinPoint joinPoint) {

    }

    @AfterReturning(value = "dataResourcePointCut()", returning = "res")
    public void afterReturning(JoinPoint joinPoint, Object res) {
        Result<?> result = (Result<?>) res;
        Class<?> dataResourceClass = getDataResourceClass(joinPoint);
        Field[] fields = dataResourceClass.getDeclaredFields();
        Set<String> allowFieldList = getAllowFieldList(joinPoint);
        Object data = result.getData();
        if (data != null) {
            if (data instanceof ArrayList<?> list) {
                rebuildObject(list, fields, allowFieldList);
            } else if (data instanceof IPage<?> page) {
                List<?> records = page.getRecords();
                rebuildObject(records, fields, allowFieldList);
            } else {
                rebuildObject(List.of(data), fields, allowFieldList);
            }
        }
        log.info("res: {}", res);
    }

    private void rebuildObject(List<?> list, Field[] fields, Set<String> allowFieldList) {
        if (list != null) {
            list.forEach(item -> {
                for (Field field : fields) {
                    field.setAccessible(true);
                    // 检查字段是否在允许列表中
                    if (!allowFieldList.contains(field.getName())) {
                        // 字段不在允许列表，将其设置为null
                        try {
                            field.set(item, null);
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            });
        }
    }

    private static Class<?> getDataResourceClass(JoinPoint joinPoint) {
        DataResource dataResource = getDataResource(joinPoint);
        return dataResource.type();
    }

    private static DataResource getDataResource(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        return method.getAnnotation(DataResource.class);
    }

    private Set<String> getAllowFieldList(JoinPoint joinPoint) {
        DataResource dataResource = getDataResource(joinPoint);
        String resourceName = dataResource.value();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert servletRequestAttributes != null;
        HttpServletRequest request = servletRequestAttributes.getRequest();
        User user = (User) request.getSession().getAttribute("user");
        Optional<Permission> permission = user.getPermissionList().stream().filter(p -> p.getCode().equals(resourceName)).findFirst();
        if (permission.isPresent()) {
            return permission.get().getAllowFields();
        }
        throw new RuntimeException(resourceName + " 无权限");
    }

}
